package com.baitiaojun.bulletscreen.websocket;

import com.alibaba.csp.sentinel.Entry;
import com.alibaba.csp.sentinel.SphU;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.fastjson.JSONObject;
import com.baitiaojun.api.domain.video.BulletScreen;
import com.baitiaojun.api.exception.ConditionException;
import com.baitiaojun.api.util.SnowFlakeUtil;
import com.baitiaojun.api.util.SpringContextUtil;
import com.baitiaojun.api.util.TokenUtil;
import com.baitiaojun.api.vo.JsonRes;
import com.baitiaojun.bulletscreen.config.RedissonConfig;
import com.baitiaojun.bulletscreen.service.BulletScreenService;
import com.baitiaojun.bulletscreen.service.MqBulletScreenService;
import com.baitiaojun.bulletscreen.util.BulletScreenConstantUtil;
import com.baitiaojun.bulletscreen.util.JsonUtil;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RBloomFilter;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @类名 WebSocketHandler
 * @描述 WebSocketHandler的作用域是多例的
 * @作者 白条君
 * @创建日期 2022/12/31 16:12
 * @版本 1.0
 */
@Getter
@Slf4j
@Component
@ServerEndpoint("/send-bullet-screen/{token}/{videoId}")
public class BulletScreenWebSocketServer {

    //定义sessionid获取session实例
    private String sessionId;

    //定义websocket会话实例
    private Session session;

    private Long userId;

    private Long clientId;

    private Long videoId;

    private static final Map<String, BulletScreenWebSocketServer> bulletScreenMap = BulletScreenConstantUtil.BULLET_SCREEN_MAP;

    private static final Map<Long, AtomicInteger> onlineCountMap = BulletScreenConstantUtil.ONLINE_COUNT_MAP;

    @OnOpen
    public void openSession(Session session, @PathParam("token") String token, @PathParam("videoId") Long videoId) throws Exception {
        if (ObjectUtils.isEmpty(videoId)) {
            throw new ConditionException("视频id不能为空");
        }
//        RedissonConfig redisson = SpringContextUtil.getBean(RedissonConfig.class);
        //通过是否为空token判断来过滤游客和登录模式
        if (StringUtils.isNotEmpty(token) || StringUtils.isNotBlank(token)) {
            //处理token验证异常
            try {
                String accessToken = token;
                if (!TokenUtil.isExpiresToken(accessToken)) {
                    accessToken = com.baitiaojun.bulletscreen.util.TokenUtil.refreshToken(token);
                }
                userId = TokenUtil.verifyToken(accessToken);
            } catch (Exception ignored) {}
        }
        //生成游客id
//        if (StringUtils.equals(token, "null")) {
//
//        }
        //获取sessionid
        this.videoId = videoId;
        this.session = session;
        sessionId = session.getId();
        //判断是否是第一次进行websocket请求
        if (bulletScreenMap.containsKey(sessionId)) {
            bulletScreenMap.remove(sessionId);
            bulletScreenMap.put(sessionId, this);
        } else {
            bulletScreenMap.put(sessionId, this);
        }
        //判断是否是第一次访问此视频请求
//        RBloomFilter<Object> bloomFilter = redisson.bloomFilter();
        AtomicInteger onlineCount;
        if (onlineCountMap.containsKey(videoId)) {
            //如果不是同一个用户才进行+1操作
            onlineCount = onlineCountMap.get(videoId);
            onlineCount.incrementAndGet();
//            if (Boolean.FALSE.equals(bloomFilter.contains(userId)) || Boolean.FALSE.equals(bloomFilter.contains(clientId))) {
//                onlineCount = onlineCountMap.get(videoId);
//                onlineCount.incrementAndGet();
//            }
        } else {
            onlineCountMap.put(videoId, new AtomicInteger(0));
            onlineCount = onlineCountMap.get(videoId);
            onlineCount.incrementAndGet();
            //添加id到布隆过滤器用来识别是否是同一个用户
//            if (ObjectUtils.isNotEmpty(userId)) {
//                bloomFilter.add(userId);
//            } else {
//                bloomFilter.add(clientId);
//            }
        }
        try {
            this.sendMsg(JsonUtil.convertJsonString("count", onlineCountMap.get(videoId)));
        } catch (Exception e) {
            log.error("连接异常");
        }
    }

    @OnClose
    public void closeConnection() {
        AtomicInteger onlineCount = onlineCountMap.get(videoId);
        if (bulletScreenMap.containsKey(sessionId)) {
            bulletScreenMap.remove(sessionId);
            //获取布隆过滤器
            RedissonConfig redisson = SpringContextUtil.getBean(RedissonConfig.class);
            RBloomFilter<Object> bloomFilter = redisson.bloomFilter();
            //如果视频观看人数是0就直接移除这个AtomicInteger
            if (onlineCount.get() == 0) {
                onlineCountMap.remove(videoId);
            } else {
                onlineCount.getAndDecrement();
            }
//            } else if (Boolean.FALSE.equals(bloomFilter.contains(userId)) || Boolean.FALSE.equals(bloomFilter.contains(clientId))) {
//                //如果不是同一个用户才进行-1操作
//                onlineCount.getAndDecrement();
//            }
        }
        log.info("连接关闭,当前在线人数:{}", onlineCount.get());
    }

    public void sendMsg(String msg) throws IOException {
        session.getBasicRemote().sendText(msg);
    }

    /**
     * 用于发送弹幕和保存当前用户的弹幕内容
     * @param message
     */
    @OnMessage
    public void onMessage(String message) throws IOException, ConditionException {
        Entry entry = null;
        try {
            entry = SphU.entry("onMessage");
            BulletScreenService bulletScreenService = SpringContextUtil.getBean(BulletScreenService.class);
            MqBulletScreenService mqBulletScreenService = SpringContextUtil.getBean(MqBulletScreenService.class);
            BulletScreen bulletScreen = JSONObject.parseObject(message, BulletScreen.class);
            //参数校验
            if (ObjectUtils.isEmpty(bulletScreen.getVideoid()) || ObjectUtils.isEmpty(bulletScreen.getContent())) {
                this.sendMsg("参数异常");
            }
            //生成弹幕记录id
            bulletScreen.setId(SnowFlakeUtil.getNextId());
            //发布消息
            mqBulletScreenService.bulletScreenSender(bulletScreen, this.sessionId);
            bulletScreen.setCreatetime(new Date());
            //保存数据到redis
            Map<String, Object> content = new HashMap<>();
            content.put("content", bulletScreen.getContent());
            content.put("createtime", bulletScreen.getCreatetime());
            bulletScreenService.syncRedis(bulletScreen.getVideoid(), content);
            //保存到mysql
            bulletScreen.setUserid(userId);
            mqBulletScreenService.syncMysqlSender(bulletScreen);
        } catch (BlockException e) {
            e.printStackTrace();
            this.sendMsg(JSONObject.toJSONString(JsonRes.builder().error(HttpStatus.SERVICE_UNAVAILABLE.value(), "系统繁忙，请稍后")));
        } finally {
            if (ObjectUtils.isNotEmpty(entry)) {
                entry.exit();
            }
        }
    }

    @OnError
    public void onError(Throwable throwable) throws IOException {
        throwable.printStackTrace();
        this.sendMsg(throwable.getMessage());
    }
}